package grpcx

import (
	"context"
	"reflect"

	"gitee.com/youkelike/zvalidation"
	"google.golang.org/grpc"
)

// 每次只用反射拿到 validator 上当前 dto 对应的验证方法，
func ValidatorInterceptor(validator *zvalidation.Validator) grpc.UnaryServerInterceptor {
	return func(ctx context.Context, rq any, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (any, error) {
		// 用反射找到验证方法，并调用
		err := validateRequest(ctx, validator, rq)
		if err != nil {
			return nil, err
		}

		// 继续处理请求
		return handler(ctx, rq)
	}
}

func validateRequest(ctx context.Context, validator *zvalidation.Validator, request any) error {
	// dto 的验证方法名都是有固定格式的：ValidateXxx，
	// 先用反射拿到 request 中 dto 名，加上 'Validate' 前缀就得到对应的验证方法名，
	dtoName := reflect.TypeOf(request).Elem().Name()
	dtoValidateName := "Validate" + dtoName

	// 再对 validator 反射
	validatorType := reflect.TypeOf(validator)
	validatorValue := reflect.ValueOf(validator)

	// dto 对应的验证方法不存在就跳过
	if _, ok := validatorType.MethodByName(dtoValidateName); !ok {
		return nil
	}
	// 用验证方法名拿到验证方法
	validationFunc := validatorValue.MethodByName(dtoValidateName)
	if validationFunc.IsValid() {
		result := validationFunc.Call([]reflect.Value{reflect.ValueOf(ctx), reflect.ValueOf(request)})
		if !result[0].IsNil() {
			return result[0].Interface().(error)
		}
	}

	return nil
}
